#ifndef __proc_h_
#define __proc_h_

#include "config/sys_param.h"
#include "config/type.h"
#include "sched/spinlock.h"

// 参考：xv6-loongarch proc 设计

// 保存上下文
struct context {
	uint64 ra;
	uint64 sp;
	uint64 s0;
	uint64 s1;
	uint64 s2;
	uint64 s3;
	uint64 s4;
	uint64 s5;
	uint64 s6;
	uint64 s7;
	uint64 s8;
	uint64 fp;
};

// 保存用户空间寄存器
struct trapframe {
	/*   0 */ uint64 ra;
	/*   8 */ uint64 tp;
	/*  16 */ uint64 sp;
	/*  24 */ uint64 a0;
	/*  32 */ uint64 a1;
	/*  40 */ uint64 a2;
	/*  48 */ uint64 a3;
	/*  56 */ uint64 a4;
	/*  64 */ uint64 a5;
	/*  72 */ uint64 a6;
	/*  80 */ uint64 a7;
	/*  88 */ uint64 t0;
	/*  96 */ uint64 t1;
	/* 104 */ uint64 t2;
	/* 112 */ uint64 t3;
	/* 120 */ uint64 t4;
	/* 128 */ uint64 t5;
	/* 136 */ uint64 t6;
	/* 144 */ uint64 t7;
	/* 152 */ uint64 t8;
	/* 160 */ uint64 r21;
	/* 168 */ uint64 fp;
	/* 176 */ uint64 s0;
	/* 184 */ uint64 s1;
	/* 192 */ uint64 s2;
	/* 200 */ uint64 s3;
	/* 208 */ uint64 s4;
	/* 216 */ uint64 s5;
	/* 224 */ uint64 s6;
	/* 232 */ uint64 s7;
	/* 240 */ uint64 s8;
	/* 248 */ uint64 kernel_sp;     // 进程的内核堆栈的顶端
	/* 256 */ uint64 kernel_trap;   // usertrap()
	/* 264 */ uint64 era;           // 保存的用户程序计数器
	/* 272 */ uint64 kernel_hartid; // 保存的内核 Tp
	/* 280 */ uint64 kernel_pgdl;   // 保存的内核 pagetable
};

enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };

struct proc {
	struct spinlock lock;	// 自旋锁

	// 使用时必须持有 p->lock 锁:
	enum procstate state;	// 进程状态
	void *chan;          	// 如果是非零值，说明正在 chan 上休眠
	int killed;          	// 如果是非零值，说明已被终结
	int xstate;          	// 退出状态将返回到父进程的等待
	int pid;             	// 进程 id

	// 使用时必须持有 wait_lock 锁:
	struct proc *parent;	// 父进程

	// 使用时必须持有 p->lock 锁，因为这些是进程的私有成员.
	uint64 kstack;              	// 内核栈虚拟地址
	uint64 sz;                  	// 进程内存大小 (bytes)
	pagetable_t pagetable;      	// 使用页表低半地址空间
	struct trapframe *trapframe;	// uservec.S 用到的数据页，需要用直接映射窗口
	struct context context;     	// swtch() 在这运行进程

	// TODO: 缺少文件系统
	// struct file *ofile[NOFILE];  // Open files
	// struct inode *cwd;           // Current directory
	char name[16]; 					// 进程名
};

struct cpu {
	struct proc *proc;      // 该 CPU 上的进程，可为空
	struct context context; // swtch() 在这进入 scheduler().
	int noff;               // push_off() 嵌套深度
	int intena;             // 记录在 push_off() 之前，中断是否被启用
};

int cpuid(void);
void exit_k(int);
int fork_k(void);
int growproc(int);
void proc_mapstacks(pagetable_t);
pagetable_t proc_pagetable(struct proc *p);
void proc_freepagetable(pagetable_t, uint64);
int kill_k(int);
struct cpu *mycpu(void);
struct proc *myproc();
void procinit(void);
void scheduler(void) __attribute__((noreturn));
void sched(void);
void sleep_k(void *, struct spinlock *);
void userinit(void);
int wait_k(uint64);
void wakeup(void *);
void yield(void);
int either_copyout(int user_dst, uint64 dst, void *src, uint64 len);
int either_copyin(void *dst, int user_src, uint64 src, uint64 len);
void procdump(void);

#endif